# vim: set fileencoding=utf-8 :
"""Vortindex: index Vorterix files"""

import json
import os
import random
import re
import shlex
import sqlite3
import subprocess
import sys
import urlparse
import urllib2


class VortVideo(object):
    """ Video from Vorterix
        :video_id: video identificator
        :url_high: video url high quality
        :url_low: video url low quality
        :name: video name
        :resize: should Vorterix allow you to rescale the video
        :url_pic: static image for the video
        :width: video with in pixels
        :height: video height in pixels
        :duration: video duration in seconds
        :xml_data: raw xml data from Vorterix
    """
    def __init__(self, video_id=None, url_high='', url_low='', name='',
                 resize=False, url_pic='', width=None, height=None,
                 duration=None, xml_data=''):
        self.__dict__.update(locals())

    def __unicode__(self):
        return u'VortVideo: %s' % self.__dict__

    def get_data(self):
        """ Gets the data for the video_id from Vorterix
        """
        data = ''
        try:
            f = urllib2.urlopen(config['base_domain']
                                + config['video_base_url'] % self.video_id)
            self.xml_data = f.read()
        except urllib2.URLError, e:
            print 'Error getting video data, video_id: %s, %s' % (video_id, e)
        return self.xml_data

    def parse_data(self, xml_data=None):
        """ Parse xml file with data about a video
            :xml_data: raw xml data from Vorterix, defaults to self.xml_data
            Returns None if there was no valid data for the video_id
        """
        if xml_data is None:
            xml_data = self.xml_data

        video_data = {}
        for data_key, data_re in config['video_data_regs'].items():
            result = data_re.search(xml_data)
            if result:
                field_data = result.groups()
                if field_data:
                    video_data[data_key] = field_data[0]

        if video_data.get('video_id', False):
            for k, v in video_data.items():
                setattr(self, k, v)
        return self if video_data.get('video_id', False) else None

    def save(self, db_conn=None):
        """ Saves video data to database
        """
        if db_conn is None:
            db_conn = get_db_conn()

        insert_query = 'INSERT INTO videos (video_id, url_high, url_low, ' \
                       + 'name, resize, url_pic, width, height, duration) ' \
                       + 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
        query_params = [self.video_id, self.url_high, self.url_low, self.name,
                        self.resize, self.url_pic, self.width, self.height,
                        self.duration]

        db_conn.execute(insert_query, query_params)
        db_conn.commit()
        db_conn.close()

    def load(self, db_conn=None):
        """ Load video data from db
        """
        if db_conn is None:
            db_conn = get_db_conn()

        select_query = 'SELECT video_id, url_high, url_low, name, ' \
                       + 'resize, url_pic, width, height, duration ' \
                       + 'FROM videos WHERE video_id = ?'
        query_params = [self.video_id]

        video_data = db_conn.execute(select_query, query_params).fetchone()
        db_conn.close()

        if video_data:
            self.video_id = int(video_data[0])
            self.url_high = video_data[1]
            self.url_low = video_data[2]
            self.name = video_data[3]
            self.resize = video_data[4]
            self.url_pic = video_data[5]
            self.width = video_data[6]
            self.height = video_data[7]
            self.duration = video_data[8]
        return self if video_data else None

    def get_full_url(self):
        """ Returns the video url_high in config['base_domain']
        """
        if self.url_high:
            full_url = urlparse.urljoin(config['base_domain'], self.url_high)
        else:
            full_url = None
        return full_url

    def play(self):
        """ Launch external player with self.url_high
        """
        video_player = config['video_player']
        video_url = self.get_full_url()
        if not video_url:
            print 'No url to play'
        else:
            print 'Playing video %s: %s' % (self.video_id, video_url)
            player_args = config['player_args'] % video_url
            command = ' '.join([video_player, player_args])
            # TODO: use shlex to split
            subprocess.Popen(command.split())

    def download(self):
        """ Downloads video file from Vorterix
        """
        download_path = config['download_path']
        try:
            os.mkdir(download_path)
        except OSError:
            pass    # path already exists

        video_url = self.get_full_url()
        if not video_url:
            print 'No url to download'
        else:
            file_extension = 'flv'
            if '.' in video_url:
                file_extension = video_url.split('.')[-1]
            filename = '.'.join([str(self.video_id), file_extension])
            video_path = os.path.join(download_path, filename)
            print 'Downloading video from %s to %s' % (video_url, video_path)
            f = urllib2.urlopen(video_url)
            with open(video_path, 'wb') as o:
                o.write(f.read())


def make_config():
    """ Read configuration from file
        and prepare config dict
    """
    with open('default-config.json') as i:
        config = json.load(i)

    try:
        with open('user-config.json') as i:
            config.update(json.load(i))
    except:
        pass  # no valid user configuration found

    config['video_data_regs'] = dict([(k, re.compile(v))
                                        for k, v in config['video_data_regs'].items()])
    return config

config = make_config()


def get_db_location():
    """ Returns the full path for the database
    """
    db_fullpath = os.path.join(config['db_path'], config['db_filename'])
    db_fullpath = os.path.abspath(db_fullpath)
    return db_fullpath


def init_db():
    """ Creates tables in database
    """
    db_fullpath = get_db_location()
    print 'Creating database %s...' % db_fullpath
    try:
        os.mkdir(os.path.dirname(db_fullpath))
    except OSError:
        pass    # path already exists

    conn = sqlite3.connect(db_fullpath)
    conn.execute('CREATE TABLE videos (video_id TEXT, url_high TEXT, url_low TEXT, name TEXT,'
                 'resize TEXT, url_pic TEXT, width INTEGER, height INTEGER, duration REAL)')
    conn.commit()
    conn.close()


def get_db_conn():
    """ Returns default datbase to store index data
        If database file doesn't exists calls init_db to create it
    """
    db_fullpath = get_db_location()
    if not os.path.exists(db_fullpath):
        init_db()
    return sqlite3.connect(db_fullpath)


def index_videos(video_ids):
    """ Index data from videos
        :video_ids: iterable with video_ids to look for
    """
    video_data_url = config['video_base_url']
    added = 0
    for video_id in video_ids:
        print 'Searching video_id %05d...' % video_id
        try:
            video = VortVideo(video_id=video_id)
            video.get_data()
            got_data = video.parse_data()
        except Exception, e:
            print '    Error looking for video data: %s' % e
        else:
            if got_data:
                video.save()
                added += 1
                print '    Ok'
            else:
                print '    No data'


def read_ids(db_conn=None):
    """ Returns all known video_ids from database
    """
    if db_conn is None:
        db_conn = get_db_conn()

    select_query = 'SELECT video_id FROM videos'

    video_ids = [x[0] for x in db_conn.execute(select_query).fetchall()]
    db_conn.close()
    return map(int, filter(lambda x: x, video_ids))


def read_videos(video_ids=None, db_conn=None):
    """ Returns a list with all the videos in the database
    """
    if db_conn is None:
        db_conn = get_db_conn()

    select_query = 'SELECT video_id, url_high, url_low, name, ' \
                   + 'resize, url_pic, width, height, duration ' \
                   + 'FROM videos'
    query_params = []
    if video_ids is not None:
        select_query += ' WHERE video_id in ?'
        query_params = [video_ids]

    rows = db_conn.execute(select_query, query_params).fetchall()
    return [VortVideo(video_id=x[0], url_high=x[1], url_low=x[2],
                        name=x[3], resize=x[4], url_pic=x[5],
                        width=x[6], height=x[7], duration=x[8])
                for x in rows]


def update_video_index(mode='new', db_conn=None):
    """ Updates the database with information from Vorterix
        :mode: new: look for the next config['new_video_ids_step'] video_ids
               missing: look for video_ids not in database and <= (max video_id in database)
               all: look for all video_ids <= (max video_id in database)
               next: look for (max video_id in database) + 1
               refresh: look only for video_ids in the database
    """
    if db_conn is None:
        db_conn = get_db_conn()
    raw_ids = [x[0] for x in
                db_conn.execute('SELECT video_id FROM videos').fetchall()]
    ids_in_db = map(int, filter(lambda x: x, raw_ids))
    max_id = 0 if len(ids_in_db) == 0 else max(ids_in_db)
    if mode == 'new':
        video_ids = range(max_id, max_id + config['new_video_ids_step'])
    elif mode == 'missing':
        video_ids = [x for x in range(max_id) if x not in ids_in_db]
    elif mode == 'all':
        video_ids = range(max_id)
    elif mode == 'next':
        video_ids = [max_id + 1]
    elif mode == 'refresh':
        video_ids = ids_in_db

    random.shuffle(video_ids)
    return index_videos(video_ids)
